/*
    Aufgabe 4) Rekursion
*/
public class Aufgabe4 {
    
    private static void printNumbersAscending(int start, int end, int divider) {
        // check for divisibility and possibly print
        if (start % divider == 0) System.out.println(start);
        // recursion exit condition
        // important note: end is evaluated and printed as well
        // before tail call gets terminated, as implied by inclusive bounds
        // important assumption: the order start <= end is never violated
        if (start == end) return;
        // recursive call incrementing start (towards end) for ascending order
        printNumbersAscending(++start, end, divider);
    }

    // functionally identical to printNumbersAscending
    private static void printNumbersDescending(int start, int end, int divider) {
        // check for divisibility and possibly print
        if (end % divider == 0) System.out.println(end);
        // recursion exit condition
        if (end == start) return;
        // recursive call decrementing end (towards start) for descending order
        printNumbersDescending(start, --end, divider);
    }

    private static int calcDigitProduct(int number) {
        // recursion exit condition 1, a number is 0
        // important assumption: number >= 0, so this is only ever the case
        // for recursive calls to calcDigitProduct
        if (number == 0) return 1;
        // recursion exit condition 2, a number is smaller than 10, in which
        // case we have the base case of a single digit
        if (number < 10) return number;
        // n recursive calls assuming n digits
        return calcDigitProduct(number % 10)
                * calcDigitProduct(number / 10);
    }
    
    private static String filterNumbersInString(String text) {
        // regex match numbers and replace with empty
        return text.replaceAll("[0-9]", "");
    }
    
    private static String reverseStringDoubleLetter(String text, char letter) {
        // recursion exit condition, there is no text to be reversed
        if (text.length() == 0) return "";
        // grep last character of text
        char last = text.charAt(text.length() - 1);
        // evaluate whether to duplicate last, in case last == letter
        String last_string = (last == letter) ?
                (Character.toString(last) + letter) :
                Character.toString(last);
        // concatenate last_string with result of recursive call
        // the order of concatenation causes our string to appear reversed
        // each recursive call processes a less long string
        return last_string
                + reverseStringDoubleLetter(text.substring(0, text.length() - 1), letter);
    }
    
    public static void main(String[] args) {
        printNumbersAscending(10, 20, 2);
        System.out.println();
        printNumbersDescending(5, 15, 3);
        System.out.println();
        
        System.out.println(calcDigitProduct(1));
        System.out.println(calcDigitProduct(102));
        System.out.println(calcDigitProduct(1234));
        System.out.println(calcDigitProduct(10000));
        System.out.println(calcDigitProduct(93842));
        System.out.println(calcDigitProduct(875943789));
        assert (calcDigitProduct(1) == 1);
        assert (calcDigitProduct(102) == 2);
        assert (calcDigitProduct(1234) == 24);
        assert (calcDigitProduct(10000) == 1);
        assert (calcDigitProduct(93842) == 1728);
        assert (calcDigitProduct(875943789) == 15240960);
        System.out.println();
        
        System.out.println(filterNumbersInString("hallo"));
        System.out.println(filterNumbersInString("Test 1 mit 45 Punkten!"));
        System.out.println(filterNumbersInString("1A1234567890B0"));
        assert(filterNumbersInString("hallo").equals("hallo"));
        assert(filterNumbersInString("Test 1 mit 45 Punkten!").equals("Test  mit  Punkten!"));
        assert(filterNumbersInString("1A1234567890B0").equals("AB"));
        assert(filterNumbersInString("a1b2c3d4e5").equals("abcde"));
        assert(filterNumbersInString("").equals(""));
        System.out.println();
    
        System.out.println(reverseStringDoubleLetter("X", 'X'));
        System.out.println(reverseStringDoubleLetter("Hallo", 'l'));
        System.out.println(reverseStringDoubleLetter("String umdrehen!", 'z'));
        assert(reverseStringDoubleLetter("X", 'X').equals("XX"));
        assert(reverseStringDoubleLetter("Hallo", 'l').equals("ollllaH"));
        assert(reverseStringDoubleLetter("String umdrehen!", 'z').equals("!neherdmu gnirtS"));
        assert(reverseStringDoubleLetter("ABACADAFA", 'A').equals("AAFAADAACAABAA"));
        assert(reverseStringDoubleLetter("", 'A').equals(""));
    }

    /*
    Zusatzfragen:
        1: Was ist Fundiertheit im Zusammenhang mit der Rekursion?
            - eine rekursive Funktion ist genau dann fundiert, wenn es eine (nicht unerreichbare) Abbruchbedingung gibt
        2: Was ist Fortschritt im Zusammenhang mit der Rekursion?
            - eine rekursive Funktion schreitet voran indem sie näher an die Abbruchbedingung kommt
            - eine fundierte rekursive Methode schreitet per Definition voran
        3: Warum benötigen Sie bei der Rekursion eine Abbruchbedingung?
            - eine rekursive Funktion würde ohne Abbruchbedingung nicht terminieren, da kein Fortschritt gemacht
              werden kann
        4: Gibt es eine Limitierung für die Rekursionstiefe?
            - wenn der call stack stetig wächst um Fortschritt zu machen, dann kann es sein, dass ein StackOverflowError
              zustande kommt
            - wenn der call stack nicht stetig wächst und dennoch Fortschritt gemacht werden kann (tail recursion),
              sprich wenn die Evaluation eines rekursiven Aufrufs nicht auf die Evaluation tieferer rekursiver Aufrufe
              angewiesen ist, dann kann die Rekursionstiefe unendlich wachsen (je nachdem wie lang einerselbst bereit
              für die Termination zu warten)
     */
}


